﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml.Linq;
using Microsoft.Build.Framework;

namespace NotifyPropertyWeaverVsPackage
{
    public class ProjectInjector
    {
        public string TargetPath { set; get; }
        public string EventInvokerName { set; get; }
        public string DependenciesDirectory { set; get; }
        public string ToolsDirectory { set; get; }
        public bool? CheckForEquality { set; get; }
        public MessageImportance? MessageImportance { set; get; }
        public bool IncludeAttributeAssembly { set; get; }
        public bool? TryToWeaveAllTypes { set; get; }
        public string ProjectFile { set; get; }
        XDocument xDocument;

        public void Execute()
        {
            new FileInfo(ProjectFile).IsReadOnly = false;
            xDocument = XDocument.Load(ProjectFile);
            TryInjectReference();
            InjectUsingTask();
            InjectWeavingTask();
            xDocument.Save(ProjectFile);
        }

        void InjectWeavingTask()
        {
            
            var target = GetOrCreateAfterCompileTarget();

            var weavingTask = target.BuildDescendants("NotifyPropertyWeaverMsBuildTask.WeavingTask")
                .FirstOrDefault();
            if (weavingTask != null)
            {
                weavingTask.Remove();
            }


            var xAttributes = new List<XAttribute>();
            if (TryToWeaveAllTypes != null)
            {
                xAttributes.Add(new XAttribute("TryToWeaveAllTypes", TryToWeaveAllTypes));
            }
            if (!string.IsNullOrWhiteSpace(EventInvokerName))
            {
                xAttributes.Add(new XAttribute("EventInvokerName", EventInvokerName));
            }
            if (CheckForEquality != null)
            {
                xAttributes.Add(new XAttribute("CheckForEquality", CheckForEquality));
            }
            if (MessageImportance != null)
            {
                xAttributes.Add(new XAttribute("MessageImportance", MessageImportance));
            }
            if (!string.IsNullOrWhiteSpace(TargetPath))
            {
                xAttributes.Add(new XAttribute("TargetPath", TargetPath));
            }
            target.Add(new XElement(XDocumentExtensions.BuildNamespace + "NotifyPropertyWeaverMsBuildTask.WeavingTask", xAttributes.ToArray()));
        }

  
        XElement GetOrCreateAfterCompileTarget()
        {
            var target = xDocument.BuildDescendants("Target")
                .Where(x => string.Equals((string) x.Attribute("Name"), "AfterCompile", StringComparison.InvariantCultureIgnoreCase)).FirstOrDefault();
            if (target == null)
            {
                target = new XElement(XDocumentExtensions.BuildNamespace + "Target", new XAttribute("Name", "AfterCompile"));
                xDocument.Root.Add(target);
            }
            return target;
        }

        void InjectUsingTask()
        {
            var count = xDocument.BuildDescendants("UsingTask")
                .Where(x => (string) x.Attribute("TaskName") == "NotifyPropertyWeaverMsBuildTask.WeavingTask").ToList();
            foreach (var xElement in count)
            {
                xElement.Remove();
            }

            xDocument.Root.Add(
                    new XElement(XDocumentExtensions.BuildNamespace + "UsingTask",
						new XAttribute("TaskName", "NotifyPropertyWeaverMsBuildTask.WeavingTask"),
						new XAttribute("AssemblyFile", Path.Combine(ToolsDirectory, @"NotifyPropertyWeaverMsBuildTask.dll"))));
        }

        void TryInjectReference()
        {
            if (!IncludeAttributeAssembly)
            {
                RemoveReference();
                return;
            }

            SelectReferences().Remove();

            GetReferences().First().Parent.Add(
                    new XElement(XDocumentExtensions.BuildNamespace + "Reference",
                        new XAttribute("Include", "NotifyPropertyWeaver"),
                        new XElement(XDocumentExtensions.BuildNamespace + "HintPath", Path.Combine(DependenciesDirectory, @"NotifyPropertyWeaver.dll"))));
        }
        
        IEnumerable<XElement> SelectReferences()
        {
            return from el in GetReferences()
                   where ((string) el.Attribute("Include")).StartsWith("NotifyPropertyWeaver")
                   select el;
        }

        IEnumerable<XElement> GetReferences()
        {
            return xDocument.BuildDescendants("Reference");
        }

        void RemoveReference()
        {
            SelectReferences().Remove();
        }

    }
}